//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Text;
using OpenLS.Core;
using OpenLS.Spreadsheet.Extensibility;

namespace OpenLS.Spreadsheet.StandardAddIn
{
    [AddInFunctions]
    public static class TextFunctions
    {
        public static object BAHTTEXT()
        {
            throw new NotImplementedException();
        }

        public static object PHONETIC()
        {
            throw new NotImplementedException();
        }

        public static object Upper(string s)
        {
            if (s == null)
                return null;
            return s.ToUpper();
        }

        public static object Lower(string s)
        {
            if (s == null)
                return null;
            return s.ToLower();
        }

        public static object Dollar(double number, [Optional(2)] int decimals)
        {
            CurrencyFormat format ;
            format = Format.Parse("$#,##0.00_);($#,##0.00)") as CurrencyFormat;

            if (format == null)
                return number.ToString();
            for (int i = 2; i < decimals; i++)
                format = (CurrencyFormat) format.IncreaseDecimal();
            for (int i = 2; i > decimals; i--)
                format = (CurrencyFormat)format.DecreaseDecimal();
            if (decimals < 0)
            {
                double factor = Math.Pow(10, -decimals);
                number = factor*Math.Round(number/factor);
            }
            return format.GetFormattedValue(number).ToString();
        }

        public static object Code(string text)
        {
            if (text == null || text.Length == 0)
                return Errors.ValueError;
            return (int) text[0];
        }

        public static string Clean(string text)
        {
            StringBuilder result = new StringBuilder();
            foreach (char c in text)
            {
                int i = c;
                if (i < 32)
                    continue;
                if (i >127 && i < 160)
                    continue;
                    result.Append(c);
            }
            return result.ToString();
        }

        public static bool Exact(string text1, string text2)
        {
            return text1 == text2;
        }

        public static object Fixed(double number, [Optional(2)] int decimals, [Optional(false)] bool no_commas)
        {
            StringBuilder sb = new StringBuilder("0.");
            if (decimals < 0 && decimals > -20) // Let's stay sane
            {
                double p = Math.Pow(10, -decimals);
                    number = p*Math.Round(number/p);
               
            }
            for (int i = 0; i < decimals; i++)
                sb.Append("0");
            if (!no_commas)
                sb.Insert(0, "#,##");
            NumberFormat fmt = new NumberFormat(sb.ToString());
            return fmt.GetFormattedValue(number).ToString();
        }

        [TreatNullsAsEmptyString]
        public static int Len(string text)
        {
            return text.Length;
        }

        public static object Mid(string text, int start_num, int num_chars)
        {
            if (num_chars < 0 || start_num <= 0)
                return Errors.ValueError;
            if (num_chars == 0)
                return string.Empty;
            if (start_num-1 >= text.Length)
                return string.Empty;
            return text.Substring(start_num - 1, Math.Min(num_chars, text.Length - start_num + 1));
        }

        public static string Proper(string text)
        {
            StringBuilder result = new StringBuilder();
            char previousChar = '\0';
            foreach (char c in text)
            {
                if (char.IsLetter(c))
                {
                    if (char.IsLetter(previousChar))
                        result.Append(char.ToLower(c));
                    else
                        result.Append(char.ToUpper(c));
                }
                else
                    result.Append(c);
                previousChar = c;
            }
            return result.ToString();
        }

        public static string Replace(string old_text, int start_num, int num_chars, string new_text)
        {
            string before = "";
            if (start_num > 1)
                before = old_text.Substring(0, start_num - 1);
            string after = "";
            if (start_num - 1 + num_chars < old_text.Length)
                after = old_text.Substring(start_num - 1 + num_chars, old_text.Length - (start_num - 1 + num_chars));
            return before + new_text + after;
        }

        public static object Rept(string text, int number_times)
        {
            StringBuilder b = new StringBuilder();
            if (number_times < 0)
                return Errors.ValueError;
            for (int i = 0; i < number_times; i++)
                b.Append(text);
            return b.ToString();
        }

        public static string Substitute(string text, string old_text, string new_text,
                                        [OptionalAttribute(-1)] int instance_num)
        {
            if (instance_num == -1)
                return text.Replace(old_text, new_text);
            StringBuilder sb = new StringBuilder();
            List<string> ss = new List<string>(Split(text, old_text));
            for (int i = 0; i < ss.Count; i++)
            {
                sb.Append(ss[i]);
                if (i == ss.Count - 1)
                    break;
                if (i == instance_num - 1)
                    sb.Append(new_text);
                else
                    sb.Append(old_text);
            }
            return sb.ToString();
        }

        private static IEnumerable<string> Split(string s, string sep)
        {
            int startIndex = 0;
            int endIndex = s.IndexOf(sep);
            while (endIndex != -1)
            {
                yield return s.Substring(startIndex, endIndex - startIndex);
                startIndex = endIndex + sep.Length;
                endIndex = s.IndexOf(sep, startIndex);
            }
            if (startIndex >= s.Length)
                yield return "";
            else
                yield return s.Substring(startIndex, s.Length - startIndex);
        }

        public static string T([AccessCellValue] object o)
        {
            if (o is ICell)
                o = ((ICell) o).Value;
            if (o == null)
                return "";
            if (o is string)
                return (string) o;
            return string.Empty;
        }

        public static string Text([WorksheetContext] IWorksheet context, [AccessCellValue(AcceptNull=true)] object value,
                                  string format_text)
        {
            Format fmt = Format.Parse(format_text);
            if (fmt is DateTimeFormat)
            {
                DateTime dt = context.ChangeType<DateTime>(value);
                return fmt.GetFormattedValue(dt).ToString();
            }
            return fmt.GetFormattedValue(value).ToString();
        }

        public static int Search (string find_text,string within_text, [Optional(1)]int start_num)
        {
            if (start_num <= 0)
                throw new ErrorException(Errors.ValueError);
            int index = within_text.ToLower().IndexOf(find_text.ToLower(), start_num - 1);
            if (index < 0)
                throw new ErrorException(Errors.ValueError);
            return index + 1;
        }



        public static string Trim( string text)
        {
            return text.Trim(); //\\
        }

        public static double Value([WorksheetContext] IWorksheet ws, string text)
        {
            double dbl;

            if (double.TryParse(text, out dbl))
                return dbl;
            DateTime dt;
            if (DateTime.TryParse(text, out dt))
                return ws.ChangeType<double>(dt);
            text = text.Trim();
            if (text.StartsWith("$") && text.Length > 1)
                text = text.Substring(1, text.Length - 1);
            double divider = 1;
            if (text.StartsWith("$") && text.Length > 1)
            {
                text = text.Substring(1, text.Length - 1);
            }
            if (text.StartsWith("%") && text.Length > 1)
            {
                divider = 100;
                text = text.Substring(1, text.Length - 1);
            }
            if (text.EndsWith("%") && text.Length > 1)
            {
                divider = 100;
                text = text.Substring(0, text.Length - 1);
            }
            if (double.TryParse(text, out dbl))
                return dbl / divider;
            throw new NotSupportedException();
                
        }

        public static string Left(string text, [OptionalAttribute(1)] int num_chars)
        {
            if (num_chars > text.Length)
                return text;
            return text.Substring(0, num_chars);
        }

        public static string Right(string text, [OptionalAttribute(1)] int num_chars)
        {
            if (num_chars > text.Length)
                return text;
            return text.Substring(text.Length - num_chars, num_chars);
        }

        public static string Char(int code)
        {
            if (code <= 0 || code >255)
                throw new ErrorException(Errors.NumError);
            return ((char) code).ToString();
        }

        public static string Concatenate([AllowToStringConversion]params string[] text)
        {
            return string.Concat(text);
        }

        public static object FindB(string find_text, string within_text, [OptionalAttribute(1)] int start_num)
        {
            return Find(find_text, within_text, start_num);
        }
        public static object Find(string find_text, string within_text, [OptionalAttribute(1)] int start_num)
        {
            int i = within_text.IndexOf(find_text, start_num - 1);
            if (i >= 0)
                return i + 1;
            return Errors.ValueError;
        }
    }
}